package com.telnikoff.gf;

import javax.swing.JComponent;

abstract public class GfGuestFactory 
{
	private String guestClassName;
	private String hostClassName;
	private String menuClassName;
	
	private GfGuestFactory(){}
	
	@SuppressWarnings("unchecked")
	private <X> X instantiateFromClassName(String className)
	{
		try
		{
			return (X) Class.forName(className).newInstance();
		}
		catch(Throwable e)
		{
			throw new GfComponentFactoryException
				("Cannot create instance of class '"+
					className+"'.", e);
		}
	}
	
	protected <T extends JComponent> GfGuest <T> makeGuestInstance()
	{
		if (null == guestClassName)
			throw new GfComponentFactoryException
				("Root component class name must be set.");
		
		return instantiateFromClassName(guestClassName);
	}
	
	protected GfHost makeHostInstance()
	{
		if (null == hostClassName)
			return new DefaultHost();
		
		return instantiateFromClassName(hostClassName);
	}
	
	protected GfMenu makeMenuInstance()
	{
		if (null == menuClassName)
			return new DefaultGfMenu();
		
		return instantiateFromClassName(menuClassName);
	}

	
	public static GfGuestFactory newFactory()
	{
		return new GfGuestFactory(){};
	}

	public void setGuestClassName(String rootComponentClassName) 
	{
		this.guestClassName = rootComponentClassName;
	}
	
	public void setHostClassName(String hostClassName)
	{
		this.hostClassName = hostClassName;
	}
	
	public <T extends JComponent> void runNewInstance()
	{
		GfGuest <T> guest = makeGuestInstance();
		GfHost host = makeHostInstance();
		guest.visit(host);
		GfMenu menu = makeMenuInstance();
		GfMenuFactory mf = menu.getFactory();
		guest.contribute(mf);
		process(guest, mf);
		host.setMenu(menu);
		host.show();
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private <T extends JComponent> void process(GfContributionCollector<T> collector, GfMenuFactory mf)
	{
		for(GfContributor<T> c : collector.getContributors())
		{
			c.contribute(collector);
			c.contribute(mf);
			
			if (c instanceof GfContributionCollector)
			{
				process((GfContributionCollector)c, mf);
			}
		}
	}
}